// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//      Gamma correction LUT stuff.
//      Functions to draw patches (by post) directly to screen.
//      Functions to blit a block to the screen.
//
//-----------------------------------------------------------------------------

#include "i_system.h"
#include "r_local.h"

#include "doomdef.h"
#include "doomdata.h"

#include "m_bbox.h"
#include "m_swap.h"

#include "v_video.h"

// Each screen is [SCREENWIDTH*SCREENHEIGHT];
byte*                           screens[5];

int                             dirtybox[4];

// Now where did these came from?
byte gammatable[5][256] =
{
    {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
     17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,
     33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
     49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,
     65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,
     81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,
     97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,
     113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
     128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
     144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
     160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
     176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
     192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
     208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
     224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
     240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255},

    {2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31,
     32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55,
     56,57,58,59,60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,77,
     78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,
     99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,
     115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129,
     130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,
     146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160,
     161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175,
     175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189,
     190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204,
     205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218,
     219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232,
     233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246,
     247,248,249,250,251,252,252,253,254,255},

    {4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42,
     43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69,
     70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93,
     94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112,
     113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
     129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144,
     144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159,
     160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173,
     174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188,
     188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201,
     202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,
     216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228,
     229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241,
     242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,
     255},

    {8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55,
     57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85,
     86,87,88,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,
     108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,
     125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140,
     141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,
     155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169,
     169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182,
     183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195,
     195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207,
     207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219,
     219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230,
     231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241,
     242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252,
     253,253,254,254,255},

    {16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76,
     78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106,
     107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,
     125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,
     142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,
     156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,
     169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181,
     182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193,
     193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203,
     204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214,
     214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224,
     224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233,
     234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242,
     243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,
     251,252,252,253,254,254,255,255}
};

int     usegamma;

//
// V_MarkRect
//
void
V_MarkRect
( int           x,
  int           y,
  int           width,
  int           height )
{
    M_AddToBox (dirtybox, x, y);
    M_AddToBox (dirtybox, x+width-1, y+height-1);
}

//
// V_CopyRect
//
void
V_CopyRect
( int           srcx,
  int           srcy,
  int           srcscrn,
  int           width,
  int           height,
  int           destx,
  int           desty,
  int           destscrn )
{
    byte*       src;
    byte*       dest;

#ifdef RANGECHECK
    if (srcx<0
        ||srcx+width >SCREENWIDTH
        || srcy<0
        || srcy+height>SCREENHEIGHT
        ||destx<0||destx+width >SCREENWIDTH
        || desty<0
        || desty+height>SCREENHEIGHT
        || (unsigned)srcscrn>4
        || (unsigned)destscrn>4)
    {
        I_Error ("Bad V_CopyRect");
    }
#endif
    V_MarkRect (destx, desty, width, height);

    src = screens[srcscrn]+SCREENWIDTH*srcy+srcx;
    dest = screens[destscrn]+SCREENWIDTH*desty+destx;

    for ( ; height>0 ; height--)
    {
        memcpy (dest, src, width);
        src += SCREENWIDTH;
        dest += SCREENWIDTH;
    }
}

//
// V_FillRect
//
void
V_FillRect
( int           x,
  int           y,
  int           scrn,
  int           width,
  int           height,
  int           color )
{
#ifdef RANGECHECK
    if (x < 0
        || x+width > SCREENWIDTH
        || y < 0
        || y+height > SCREENHEIGHT
        || (unsigned)scrn > 4)
    {
        I_Error ("Bad V_FillRect");
    }
#endif
    V_MarkRect (x, y, width, height);

    byte* dest = screens[scrn]+SCREENWIDTH*y+x;
    for (int i = 0; i < height; ++i)
    {
        memset (dest, color, width);
        dest += SCREENWIDTH;
    }
}

static inline void V_DrawPatchScaledInternal (int x,
                                              int y,
                                              int scrn,
                                              patch_t* patch,
                                              boolean flip)
{
    x -= TOSCREENX (SHORT (patch->leftoffset));
    y -= TOSCREENY (SHORT (patch->topoffset));
    const int src_w = SHORT (patch->width);
    const int src_h = SHORT (patch->height);
    const int w = TOSCREENX (src_w);
    const int h = TOSCREENY (src_h);
#ifdef RANGECHECK
    if (x < 0 || x + w > SCREENWIDTH || y < 0 || y + h > SCREENHEIGHT ||
        (unsigned)scrn > 4)
    {
        fprintf (stderr, "Patch at %d,%d exceeds LFB\n", x, y);
        // No I_Error abort - what is up with TNT.WAD?
        fprintf (stderr, "V_DrawPatchScaled: bad patch (ignored)\n");
        return;
    }
#endif

    if (!scrn)
        V_MarkRect (x, y, w, h);

    byte* desttop = screens[scrn] + y * SCREENWIDTH + x;

    // 16.16-bit fixed point resolution scaling (nearest neighbour).
    const fixed_t step_x = (BASE_WIDTH << 16) / SCREENWIDTH;
    const fixed_t step_y = (BASE_HEIGHT << 16) / SCREENHEIGHT;
    fixed_t col_fixed = 0;
    for (int u = 0; u < w; ++u, ++desttop, col_fixed += step_x)
    {
        int col = col_fixed >> 16;
        if (flip)
            col = src_w - 1 - col;

        column_t* column =
            (column_t*)((byte*)patch + LONG (patch->columnofs[col]));

        // Step through the posts in a column.
        // TODO(m): The scaling isn't sub-pixel accurate.
        post_t* post = column;
        while (post->topdelta != 0xff)
        {
            const byte* src = (byte*)post + 3;
            byte* dst = desttop + TOSCREENY ((int)post->topdelta) * SCREENWIDTH;
            int count = TOSCREENY ((int)post->length);
#if defined(__MRISC32_VECTOR_OPS__)
            // This vectorized routine takes <5 clock-cycles per pixel.
            const unsigned stride = SCREENWIDTH;
            unsigned count_left, step_y_N, dst_incr;
            byte* dst_ptr;
            __asm__ volatile(
                "    ble     %[count], 2f\n"
                "    getsr   vl, #0x10\n"
                "    mov     %[count_left], %[count]\n"
                "    mov     %[dst_ptr], %[dst]\n"
                "    mul     %[step_y_N], vl, %[step_y]\n"
                "    mul     %[dst_incr], vl, %[stride]\n"
                "    ldea    v1, [z, %[step_y]]\n"
                "1:\n"
                "    min     vl, vl, %[count_left]\n"
                "    sub     %[count_left], %[count_left], vl\n"
                "    asr     v2, v1, #16\n"
                "    ldub    v2, [%[src], v2]\n"
                "    stb     v2, [%[dst_ptr], %[stride]]\n"
                "    ldea    %[dst_ptr], [%[dst_ptr], %[dst_incr]]\n"
                "    add     v1, v1, %[step_y_N]\n"
                "    bnz     %[count_left], 1b\n"
                "2:"
                : [count_left] "=&r"(count_left),
                  [step_y_N] "=&r"(step_y_N),
                  [dst_incr] "=&r"(dst_incr),
                  [dst_ptr] "=&r"(dst_ptr)
                : [dst] "r"(dst),
                  [src] "r"(src),
                  [count] "r"(count),
                  [step_y] "r"(step_y),
                  [stride] "r"(stride)
                : "vl", "v1", "v2"
                );
#else
            fixed_t row_fixed = 0;
            for (int v = 0; v < count; ++v)
            {
                *dst = src[row_fixed >> 16];
                dst += SCREENWIDTH;
                row_fixed += step_y;
            }
#endif
            post = (post_t*)((byte*)post + post->length + 4);
        }
    }
}

static void V_DrawPatchInternal (int x,
                                 int y,
                                 int scrn,
                                 patch_t* patch,
                                 boolean flip)
{
    y -= SHORT (patch->topoffset);
    x -= SHORT (patch->leftoffset);
#ifdef RANGECHECK
    if (x < 0 || x + SHORT (patch->width) > SCREENWIDTH || y < 0 ||
        y + SHORT (patch->height) > SCREENHEIGHT || (unsigned)scrn > 4)
    {
        fprintf (stderr, "Patch at %d,%d exceeds LFB\n", x, y);
        // No I_Error abort - what is up with TNT.WAD?
        fprintf (stderr, "V_DrawPatch: bad patch (ignored)\n");
        return;
    }
#endif

    if (!scrn)
        V_MarkRect (x, y, SHORT (patch->width), SHORT (patch->height));

    int w = SHORT (patch->width);

    byte* desttop = screens[scrn] + y * SCREENWIDTH + x;
    for (int u = 0; u < w; ++x, ++u, ++desttop)
    {
        int col = u;
        if (flip)
            col = w - 1 - col;

        column_t* column =
            (column_t*)((byte*)patch + LONG (patch->columnofs[col]));

        // Step through the posts in a column.
        while (column->topdelta != 0xff)
        {
            const byte* src = (byte*)column + 3;
            byte* dst = desttop + column->topdelta * SCREENWIDTH;
            int count = column->length;
#if defined(__MRISC32_VECTOR_OPS__)
            // This vectorized routine takes <3 clock-cycles per pixel.
            const unsigned stride = SCREENWIDTH;
            unsigned count_left, dst_incr;
            byte* src_ptr, dst_ptr;
            __asm__ volatile(
                "    ble     %[count], 2f\n"
                "    getsr   vl, #0x10\n"
                "    mov     %[count_left], %[count]\n"
                "    mov     %[src_ptr], %[src]\n"
                "    mov     %[dst_ptr], %[dst]\n"
                "    mul     %[dst_incr], vl, %[stride]\n"
                "1:\n"
                "    min     vl, vl, %[count_left]\n"
                "    sub     %[count_left], %[count_left], vl\n"
                "    ldub    v1, [%[src_ptr], #1]\n"
                "    stb     v1, [%[dst_ptr], %[stride]]\n"
                "    ldea    %[src_ptr], [%[src_ptr], vl]\n"
                "    ldea    %[dst_ptr], [%[dst_ptr], %[dst_incr]]\n"
                "    bnz     %[count_left], 1b\n"
                "2:"
                : [count_left] "=&r"(count_left),
                  [dst_incr] "=&r"(dst_incr),
                  [src_ptr] "=&r"(src_ptr),
                  [dst_ptr] "=&r"(dst_ptr)
                : [dst] "r"(dst),
                  [src] "r"(src),
                  [count] "r"(count),
                  [stride] "r"(stride)
                : "vl", "v1"
                );
#else
            for (int v = 0; v < count; ++v)
            {
                *dst = *src++;
                dst += SCREENWIDTH;
            }
#endif
            column = (column_t*)((byte*)column + column->length + 4);
        }
    }
}

//
// V_DrawPatch
// Masks a column based masked pic to the screen.
//
void V_DrawPatchScaled (int x, int y, int scrn, patch_t* patch)
{
    V_DrawPatchScaledInternal (x, y, scrn, patch, false);
}

//
// V_DrawPatch
// Masks a column based masked pic to the screen.
//
void V_DrawPatch (int x, int y, int scrn, patch_t* patch)
{
    V_DrawPatchInternal (x, y, scrn, patch, false);
}

//
// V_DrawPatchFlipped
// Masks a column based masked pic to the screen.
// Flips horizontally, e.g. to mirror face.
//
void V_DrawPatchFlipped (int x, int y, int scrn, patch_t* patch)
{
    V_DrawPatchInternal (x, y, scrn, patch, true);
}

//
// V_DrawPatchDirect
// Draws directly to the screen on the pc.
//
void V_DrawPatchDirect (int x, int y, int scrn, patch_t* patch)
{
    V_DrawPatchInternal (x, y, scrn, patch, false);
}

//
// V_DrawBlock
// Draw a linear block of pixels into the view buffer.
//
void
V_DrawBlock
( int           x,
  int           y,
  int           scrn,
  int           width,
  int           height,
  byte*         src )
{
    byte*       dest;

#ifdef RANGECHECK
    if (x<0
        ||x+width >SCREENWIDTH
        || y<0
        || y+height>SCREENHEIGHT
        || (unsigned)scrn>4 )
    {
        I_Error ("Bad V_DrawBlock");
    }
#endif

    V_MarkRect (x, y, width, height);

    dest = screens[scrn] + y*SCREENWIDTH+x;

    while (height--)
    {
        memcpy (dest, src, width);
        src += width;
        dest += SCREENWIDTH;
    }
}

//
// V_GetBlock
// Gets a linear block of pixels from the view buffer.
//
void
V_GetBlock
( int           x,
  int           y,
  int           scrn,
  int           width,
  int           height,
  byte*         dest )
{
    byte*       src;

#ifdef RANGECHECK
    if (x<0
        ||x+width >SCREENWIDTH
        || y<0
        || y+height>SCREENHEIGHT
        || (unsigned)scrn>4 )
    {
        I_Error ("Bad V_DrawBlock");
    }
#endif

    src = screens[scrn] + y*SCREENWIDTH+x;

    while (height--)
    {
        memcpy (dest, src, width);
        src += SCREENWIDTH;
        dest += width;
    }
}

//
// V_Init
//
void V_Init (void)
{
    int         i;
    byte*       base;

    // stick these in low dos memory on PCs

    base = I_AllocLow (SCREENWIDTH*SCREENHEIGHT*4);

    for (i=0 ; i<4 ; i++)
        screens[i] = base + i*SCREENWIDTH*SCREENHEIGHT;
}
